home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 1.toast / Sample Code / Archive / Networking / OTTCPWillDial / OTTCPWillDial.c next >
Encoding:
Text File  |  2000-09-28  |  20.3 KB  |  731 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        OTTCPWillDial.c
  3.  
  4.     Contains:    Library to determine whether open a TCP endpoint will
  5.                 dial the modem.
  6.  
  7.     Written by:    Quinn "The Eskimo!"
  8.  
  9.     Copyright:    © 1998 by Apple Computer, Inc., all rights reserved.
  10.  
  11.     Change History (most recent first):
  12.  
  13.     You may incorporate this sample code into your applications without
  14.     restriction, though the sample code has been provided "AS IS" and the
  15.     responsibility for its operation is 100% yours.  However, what you are
  16.     not permitted to do is to redistribute the source as "DSC Sample Code"
  17.     after having made changes. If you're going to re-distribute the source,
  18.     we require that you make it clear in the source that the code was
  19.     descended from Apple Sample Code, but that you've made changes.
  20. */
  21.  
  22. /////////////////////////////////////////////////////////////////
  23.  
  24. #define qDebug 1
  25.  
  26. /////////////////////////////////////////////////////////////////
  27. // Pick up lots of OT interfaces.
  28.  
  29. #import <OpenTransport.h>
  30. #import <OpenTptInternet.h>
  31. #import <OpenTptLinks.h>
  32. #import <OTDebug.h>
  33.  
  34. /////////////////////////////////////////////////////////////////
  35. // Pick up standard system interfaces.
  36.  
  37. #import <CodeFragments.h>
  38. #import <Resources.h>
  39. #import <Errors.h>
  40. #import <Folders.h>
  41.  
  42. /////////////////////////////////////////////////////////////////
  43. // Pick up OT configuration database stuff.
  44.  
  45. #import <NetworkSetup.h>
  46.  
  47. /////////////////////////////////////////////////////////////////
  48. // Pick up our own header file.
  49.  
  50. #import "OTTCPWillDial.h"
  51.  
  52. /////////////////////////////////////////////////////////////////
  53. // Default OTDebugStr prototype, because it's not in the interfaces.
  54.  
  55. extern void OTDebugStr(const char *message);
  56.  
  57. /////////////////////////////////////////////////////////////////
  58. // Common code to parse 'iitf' preferences.
  59.  
  60. // The structure of an 'iitf' is the same regardless of whether it
  61. // comes from a resource or from the configuration database.
  62.  
  63. // An 'iitf' preference consists of a UInt16 count followed
  64. // by 0 or more interface specifications.  Each interface
  65. // specification is a variable length data structure, with
  66. // some fixed length and some variable length fields.
  67. // This structure is used to represent an interface as 
  68. // a fixed size data structure, much more suitable for
  69. // C programming.
  70.  
  71. // In current versions of OT, only one interface is allowed.
  72.  
  73. struct TCPiitfPref {
  74.     UInt8    fActive;
  75.     InetHost fIPAddress;
  76.     InetHost fSubnetMask;
  77.     Str255   fAppleTalkZone;
  78.     UInt8    fPath[36];            // Pascal string
  79.     UInt8    fModuleName[31];    // Pascal string
  80.     UInt32   fFramingFlags;
  81. };
  82. typedef struct TCPiitfPref TCPiitfPref;
  83.  
  84. static void UnpackIITF(Ptr *buffer, TCPiitfPref *unpackedIITF)
  85.     // This routine unpacks an interface from an 'iitf' preference
  86.     // into a TCPiitfPref.  *buffer must point to the beginning
  87.     // of the interface, ie two bytes into the pref data if
  88.     // if you're extracting the first interface.  *buffer
  89.     // is updated to point to the byte after the last byte
  90.     // parsed, so you can parse multiple interfaces by
  91.     // repeatedly calling this routine.
  92. {
  93.     UInt8 *cursor;
  94.     
  95.     cursor = (UInt8 *) *buffer;
  96.     
  97.     unpackedIITF->fActive = *cursor;
  98.     cursor += sizeof(UInt8);
  99.     unpackedIITF->fIPAddress = *((InetHost *) cursor);
  100.     cursor += sizeof(InetHost);
  101.     unpackedIITF->fSubnetMask = *((InetHost *) cursor);
  102.     cursor += sizeof(InetHost);
  103.     BlockMoveData(cursor, unpackedIITF->fAppleTalkZone, *cursor + 1);
  104.     cursor += (*cursor + 1);
  105.     BlockMoveData(cursor, unpackedIITF->fPath, 36);
  106.     cursor += 36;
  107.     BlockMoveData(cursor, unpackedIITF->fModuleName, 32);
  108.     cursor += 32;
  109.     unpackedIITF->fFramingFlags = *((UInt32 *) cursor);
  110.     cursor += sizeof(UInt32);
  111.  
  112.     *buffer = (Ptr) cursor;
  113. }
  114.  
  115. static OSStatus GetPortNameFromIITF(Ptr buffer, SInt32 prefSize, char *portName)
  116.     // This routine takes the address and size of an 'iitf' preference
  117.     // and extracts the port name from the first interface.
  118. {
  119.     OSStatus err;
  120.     UInt16 interfaceCount;
  121.     Ptr cursor;
  122.     TCPiitfPref firstInterface;
  123.     UInt8 portNameLength;
  124.     
  125.     // Get the count of interfaces, checking for possibly bogus
  126.     // preference data.
  127.     
  128.     err = noErr;
  129.     if (prefSize < sizeof(UInt16)) {
  130.         err = -1;
  131.     }
  132.     if (err == noErr) {
  133.         interfaceCount = *((UInt16 *)buffer);
  134.         if (interfaceCount < 1) {
  135.             err = -1;
  136.         }
  137.     }
  138.     
  139.     // Unpack the first interface out of the 'iitf'.
  140.     
  141.     if (err == noErr) {
  142.         cursor = buffer + sizeof(UInt16);
  143.         UnpackIITF(&cursor, &firstInterface);
  144.  
  145.         OTAssert("GetPortNameFromIITF: Did not consume correct number of bytes",
  146.                     interfaceCount > 1 || (cursor == buffer + prefSize) );
  147.     }
  148.     
  149.     // Copy the port name out of the unpacked interface.
  150.     
  151.     if (err == noErr) {
  152.         portNameLength = firstInterface.fPath[0];
  153.         if ( portNameLength > kMaxProviderNameLength) {
  154.             err = -1;
  155.         } else {
  156.  
  157.             // Poor Man's C2PString avoids me having to figure
  158.             // out which wacky library CodeWarrior wants me to link with
  159.             // today!
  160.             
  161.             BlockMoveData(firstInterface.fPath + 1, portName, portNameLength);
  162.             portName[ portNameLength ] = 0;
  163.         }
  164.     }
  165.  
  166.     return err;
  167. }
  168.  
  169. /////////////////////////////////////////////////////////////////
  170. // OT configuration database implementation.
  171.  
  172. static OSStatus GetFixedSizePref(CfgDatabaseRef ref, const CfgEntityRef *entityID, OSType prefType,
  173.                         void *buffer, ByteCount prefSize)
  174.     // This routine gets a fixed size preference out of
  175.     // the configuration database described by ref.  entityID
  176.     // is the entity containing the preference.  prefType is the
  177.     // type of preference within the entity.  buffer is the address
  178.     // where the preference data should be put.  prefSize is the size
  179.     // of the buffer, and the routine validates that the preference
  180.     // is exactly that size.
  181. {
  182.     OSStatus err;
  183.     OSStatus err2;
  184.     CfgEntityAccessID prefsRefNum;
  185.     ByteCount actualPrefSize;
  186.     
  187.     OTAssert("GetFixedSizePref: paramErr", buffer != nil);
  188.  
  189.     // Open the entity, read out the preference, and then
  190.     // close it down.
  191.     
  192.     err = OTCfgOpenPrefs(ref, entityID, false, &prefsRefNum);
  193.     if (err == noErr) {
  194.         err = OTCfgGetPrefsSize(prefsRefNum, prefType, &actualPrefSize);
  195.         if (err == noErr && actualPrefSize != prefSize) {
  196.             err = -1;
  197.         }
  198.         if (err == noErr) {
  199.             err = OTCfgGetPrefs(prefsRefNum, prefType, buffer, prefSize);
  200.         }
  201.     
  202.         err2 = OTCfgClosePrefs(prefsRefNum);
  203.         if (err == noErr) {
  204.             err = err2;
  205.         }
  206.     }
  207.     
  208.     return err;
  209. }
  210.  
  211. static OSStatus GetPref(CfgDatabaseRef ref, const CfgEntityRef *entityID, OSType prefType,
  212.                         void **buffer, ByteCount *prefSize)
  213.     // This routine gets a variable size preference out of
  214.     // the configuration database described by ref.  entityID
  215.     // is the entity containing the preference.  prefType is the
  216.     // type of preference within the entity.  buffer is the address
  217.     // a pointer where the address of the newly allocated preference
  218.     // buffer should be put.  prefSize is the address of a variable
  219.     // where the size of the newly allocated preference should be.
  220.     // 
  221.     // The caller is responsible for disposing of the preference buffer
  222.     // using OTFreeMem.  If the routine fails, no preference buffer is 
  223.     // returned.
  224. {
  225.     OSStatus err;
  226.     OSStatus err2;
  227.     CfgEntityAccessID prefsRefNum;
  228.     
  229.     OTAssert("GetPref: paramErr", buffer != nil);
  230.  
  231.     // Open the entity, read out the preference, and then
  232.     // close it down.
  233.     
  234.     *buffer = nil;    
  235.     err = OTCfgOpenPrefs(ref, entityID, false, &prefsRefNum);
  236.     if (err == noErr) {
  237.         err = OTCfgGetPrefsSize(prefsRefNum, prefType, prefSize);
  238.  
  239.         if (err == noErr) {
  240.             *buffer = OTAllocMem(*prefSize);
  241.             if (*buffer == nil) {
  242.                 err = kOTOutOfMemoryErr;
  243.             }
  244.         }
  245.         if (err == noErr) {
  246.             err = OTCfgGetPrefs(prefsRefNum, prefType, *buffer, *prefSize);
  247.         }
  248.     
  249.         err2 = OTCfgClosePrefs(prefsRefNum);
  250.         if (err == noErr) {
  251.             err = err2;
  252.         }
  253.     }
  254.     
  255.     // Clean up.
  256.     
  257.     if (err != noErr && *buffer != nil) {
  258.         OTFreeMem(*buffer);
  259.         *buffer = nil;
  260.     }
  261.     return err;
  262. }
  263.  
  264. static OSStatus GetEntityList(CfgDatabaseRef ref, CfgAreaID area,
  265.                                 OSType class, OSType type,
  266.                                 CfgEntityRef **entityIDs, ItemCount *entityCount)
  267.     // This routine gets a list of all the entities that match
  268.     // class and type in the specified area of the specified database.
  269.     // It allocates a buffer (using OTAllocMem) to hold the CfgEntityRef's
  270.     // for the result and sets *entityIDs to point to the buffer.  It sets
  271.     // entityCount to the number of CfgEntityRef's in the buffer.
  272. {
  273.     OSStatus err;
  274.     CfgEntityInfo *junkEntityInfos;
  275.  
  276.     OTAssert("GetEntityList: paramErr", entityIDs != nil);
  277.     OTAssert("GetEntityList: paramErr", entityCount != nil);
  278.     
  279.     *entityIDs = nil;
  280.     junkEntityInfos = nil;
  281.     
  282.     err = OTCfgGetEntitiesCount(ref, area, class, type, entityCount);
  283.     if (err == noErr) {
  284.         *entityIDs = OTAllocMem(*entityCount * sizeof(CfgEntityRef));
  285.         junkEntityInfos = OTAllocMem(*entityCount * sizeof(CfgEntityInfo));
  286.         if (*entityIDs == nil || junkEntityInfos == nil) {
  287.             err = kOTOutOfMemoryErr;
  288.         }
  289.     }
  290.     if (err == noErr) {
  291.     
  292.         // I'm not sure whether you can pass nil to the entityInfos parameter
  293.         // of OTCfgGetEntitiesList, so for the moment I'm passing in a valid
  294.         // buffer.  I'll fix this up pending confirmation from engineering
  295.         // that nil is OK.
  296.         
  297.         err = OTCfgGetEntitiesList(ref, area, 
  298.                     class, type, 
  299.                     entityCount, *entityIDs, junkEntityInfos);
  300.     }
  301.     
  302.     // Clean up.
  303.     
  304.     if (junkEntityInfos != nil) {
  305.         OTFreeMem(junkEntityInfos);
  306.     }
  307.     
  308.     if (err != noErr) {
  309.         if (*entityIDs != nil) {
  310.             OTFreeMem(*entityIDs);
  311.             *entityIDs = nil;
  312.         }
  313.     }
  314.     return err;
  315. }
  316.  
  317. static OSStatus GetInfoForTCPEntity(CfgDatabaseRef ref, const CfgEntityRef *entityID,
  318.                                     Boolean *enabled, char *portName)
  319.     // This routine returns the enabled status and port name
  320.     // for the TCP/IP preferences entity described by entityID
  321.     // in the ref database.
  322. {    
  323.     OSStatus err;
  324.     SInt16 enabledInt;
  325.     Ptr buffer;
  326.     ByteCount prefSize;
  327.  
  328.     buffer = nil;
  329.  
  330.     // First return enabled using the simple API.
  331.     
  332.     err = GetFixedSizePref(ref, entityID, 'unld', &enabledInt, sizeof(SInt16));
  333.     if (err == noErr) {
  334.         *enabled = (enabledInt != 3);
  335.     }
  336.     
  337.     // Now return the port name.  Now call the variable sized
  338.     // API to get the 'iitf' resource and then extract the port name 
  339.     // from the preference buffer.
  340.     
  341.     if (err == noErr) {
  342.         err = GetPref(ref, entityID, 'iitf', &buffer, &prefSize);
  343.     }
  344.     if (err == noErr) {
  345.         err = GetPortNameFromIITF(buffer, prefSize, portName);
  346.     }
  347.     
  348.     // Clean up.
  349.     
  350.     if (buffer != nil) {
  351.         OTFreeMem(buffer);
  352.     }
  353.     return err;
  354. }
  355.  
  356. static OSStatus FindActiveSet(CfgDatabaseRef ref, CfgAreaID area, CfgEntityRef *activeSet)
  357.     // This routine finds the entity ref of the active set entity
  358.     // in the database.  It works by finding all the set entities
  359.     // (there is generally only one in the current OT implementation)
  360.     // and checks each one for the active bit set in its flags.
  361.     // It returns the first set that claims to be active.
  362. {
  363.     OSStatus err;
  364.     ItemCount setCount;
  365.     CfgEntityRef *setEntities;
  366.     Boolean found;
  367.     ItemCount thisSetIndex;
  368.     CfgSetsStruct thisStruct;
  369.  
  370.     setEntities = nil;
  371.  
  372.     err = GetEntityList(ref, area, kOTSetOfSettingsClass, kOTSetOfSettingsType, &setEntities, &setCount);
  373.     if (err == noErr) {
  374.         thisSetIndex = 0;
  375.         found = false;
  376.         while (err == noErr && thisSetIndex < setCount && ! found) {
  377.             err = GetFixedSizePref(ref, &setEntities[thisSetIndex], kPrefsTypeStruct,
  378.                             &thisStruct, sizeof(thisStruct));
  379.             if (err == noErr) {
  380.                 found = ((thisStruct.fFlags & (1 << kSetsIndexActive)) != 0);
  381.                 if ( ! found ) {
  382.                     thisSetIndex += 1;
  383.                 }
  384.             }
  385.         }
  386.         if (err == noErr && ! found) {
  387.             err = -1;
  388.         }
  389.     }
  390.     if (err == noErr) {
  391.         *activeSet = setEntities[thisSetIndex];
  392.     }
  393.  
  394.     // Clean up.
  395.     
  396.     if (setEntities != nil) {
  397.         OTFreeMem(setEntities);
  398.     }
  399.     
  400.     return err;
  401. }
  402.  
  403. static OSStatus FindCurrentTCPEntity(CfgDatabaseRef ref, CfgAreaID area, CfgEntityRef *currentTCPEntity)
  404.     // This routine finds the current active TCP/IP connection entity.
  405.     // It does this by first looking up the active set, then getting
  406.     // the list of entities out of the active set, then searching
  407.     // through that list of entities for the first TCP/IP connection
  408.     // entity.
  409. {
  410.     OSStatus err;
  411.     CfgEntityRef activeSet;
  412.     CfgSetsVector *vectorPrefData;
  413.     ByteCount vectorPrefSize;
  414.     Boolean found;
  415.     ItemCount thisElementIndex;
  416.     CfgEntityInfo thisEntityInfo;
  417.     
  418.     vectorPrefData = nil;
  419.  
  420.     err = FindActiveSet(ref, area, &activeSet);
  421.     if (err == noErr) {
  422.         err = GetPref(ref, &activeSet, kPrefsTypeVector,
  423.                         &vectorPrefData, &vectorPrefSize);
  424.     }
  425.     if (err == noErr) {
  426.  
  427.         // The kOTSetOfSettingsClass/kOTSetOfSettingsType preference
  428.         // data is a count of elements followed by an array of that
  429.         // many elements.  We walk index through the array looking
  430.         // for the first TCP/IP connection entity.
  431.         
  432.         thisElementIndex = 0;
  433.         found = false;
  434.         while ( thisElementIndex < vectorPrefData->fCount && ! found ) {
  435.             thisEntityInfo = vectorPrefData->fElements[thisElementIndex].fEntityInfo;
  436.             found = (thisEntityInfo.fClass == kOTNetworkConnectionClass) 
  437.                         && (thisEntityInfo.fType == kOTTCPv4NetworkConnection);
  438.             if (found) {
  439.                 *currentTCPEntity = vectorPrefData->fElements[thisElementIndex].fEntityRef;
  440.                 
  441.                 // A weird misfeature of kOTSetOfSettingsClass/kOTSetOfSettingsType 
  442.                 // preference is that the CfgEntityRef's it holds have their area
  443.                 // (ie fLoc) set to a bogus area ID.  [It's actually the area ID
  444.                 // of the temporary area generated when the person who wrote the
  445.                 // set called OTCfgBeginAreaModifications.]  So we have to reset
  446.                 // this to the current area before returning it to our caller.
  447.                 
  448.                 currentTCPEntity->fLoc = area;
  449.             } else {
  450.                 thisElementIndex += 1;
  451.             }
  452.         }
  453.         if ( err == noErr && ! found ) {
  454.             err = -3;
  455.         }
  456.     }
  457.     
  458.     // Clean up.
  459.     
  460.     if (vectorPrefData != nil) {
  461.         OTFreeMem(vectorPrefData);
  462.     }
  463.     return err;
  464. }
  465.  
  466. static OSStatus GetTCPInfoUsingAPI(Boolean *enabled, char *portName)
  467.     // The high-level entry point into the configuration database
  468.     // implementation.  We open the database, find the current
  469.     // TCP entity and read the info we need out of that entity.
  470. {
  471.     OSStatus err;
  472.     OSStatus err2;
  473.     CfgDatabaseRef ref;
  474.     CfgAreaID currentArea;
  475.     CfgEntityRef currentTCPEntity;
  476.     
  477.     err = OTCfgOpenDatabase(&ref);
  478.     if (err == noErr) {
  479.         err = OTCfgGetCurrentArea(ref, ¤tArea);
  480.         if (err == noErr) {
  481.             err = OTCfgOpenArea(ref, currentArea);
  482.             if (err == noErr) {
  483.                 err = FindCurrentTCPEntity(ref, currentArea, ¤tTCPEntity);
  484.                 if (err == noErr) {
  485.                     err = GetInfoForTCPEntity(ref, ¤tTCPEntity, enabled, portName);
  486.                 }
  487.                 
  488.                 err2 = OTCfgCloseArea(ref, currentArea);
  489.                 if (err == noErr) {
  490.                     err = err2;
  491.                 }
  492.             }
  493.         }
  494.     
  495.         err2 = OTCfgCloseDatabase(&ref);
  496.         if (err == noErr) {
  497.             err = err2;
  498.         }
  499.     }
  500.     return err;
  501. }
  502.  
  503. /////////////////////////////////////////////////////////////////
  504. // Implementation that reads the TCP/IP Preferences file directly.
  505.  
  506. // You have to search for the preferences file by type and creator
  507. // because the name will be different on localised systems.
  508.  
  509. enum {
  510.     kOTTCPPrefFileType = 'pref',
  511.     kOTTCPPrefFileCreator = 'ztcp'
  512. };
  513.  
  514. static OSStatus FindTCPPrefFile(FSSpec *fss)
  515.     // This routine scans the Preferences folder looking
  516.     // for the "TCP/IP Preferences" file by type and creator.
  517. {
  518.     OSStatus err;
  519.     Boolean found;
  520.     CInfoPBRec cpb;
  521.     SInt16 index;
  522.     
  523.     err = FindFolder(kOnSystemDisk, kPreferencesFolderType, kCreateFolder, &fss->vRefNum, &fss->parID);
  524.     if (err == noErr) {
  525.         found = false;
  526.         index = 1;
  527.         do {
  528.             cpb.hFileInfo.ioVRefNum = fss->vRefNum;
  529.             cpb.hFileInfo.ioDirID = fss->parID;
  530.             cpb.hFileInfo.ioNamePtr = fss->name;
  531.             cpb.hFileInfo.ioFDirIndex = index;
  532.             err = PBGetCatInfoSync(&cpb);
  533.             if (err == noErr) {
  534.                 found = (    cpb.hFileInfo.ioFlFndrInfo.fdType == kOTTCPPrefFileType &&
  535.                             cpb.hFileInfo.ioFlFndrInfo.fdCreator == kOTTCPPrefFileCreator );
  536.             }
  537.             index += 1;
  538.         } while (err == noErr & ! found);
  539.     }
  540.     return err;
  541. }
  542.  
  543. static OSStatus CheckResError(void *testH)
  544.     // A trivial wrapper routine for ResError,
  545.     // which is too lame to report an error code
  546.     // in all cases when GetResource fails.
  547. {
  548.     OSStatus err;
  549.  
  550.     err = ResError();
  551.     if (err == noErr && testH == nil) {
  552.         err = resNotFound;
  553.     }
  554.     return err;
  555. }
  556.  
  557. static OSStatus GetTCPInfoFromFile(Boolean *enabled, char *portName)
  558.     // This is the high-level entry point into the direct file
  559.     // access implementation.  It simply finds the preferences
  560.     // file and reads the preferences out directly.
  561. {
  562.     OSStatus err;
  563.     FSSpec fss;
  564.     SInt16 oldResFile;
  565.     SInt16 prefResFile;
  566.     Handle currentConfigResourceH;
  567.     Handle unldResource;
  568.     Handle iitfResource;
  569.     SInt8  s;
  570.     
  571.     oldResFile = CurResFile();
  572.     
  573.     err = FindTCPPrefFile(&fss);
  574.     if (err == noErr) {
  575.         prefResFile = FSpOpenResFile(&fss, fsRdPerm);
  576.         err = ResError();
  577.     }
  578.     if (err == noErr) {
  579.  
  580.         currentConfigResourceH = Get1Resource('ccfg', 1);
  581.         err = CheckResError(currentConfigResourceH);
  582.  
  583.         if (err == noErr && GetHandleSize(currentConfigResourceH) != sizeof(SInt16) ) {
  584.             OTDebugBreak("GetTCPInfoFromFile: 'ccfg' is of the wrong size");
  585.             err = -1;
  586.         }
  587.  
  588.         if (err == noErr) {
  589.             unldResource = Get1Resource('unld', **( (SInt16 **) currentConfigResourceH));
  590.             err = CheckResError(unldResource);
  591.         }
  592.         if (err == noErr) {
  593.             *enabled = ( **((SInt16 **) unldResource) != 3);
  594.         }
  595.  
  596.         if (err == noErr) {
  597.             iitfResource = Get1Resource('iitf', **( (SInt16 **) currentConfigResourceH));
  598.             err = CheckResError(iitfResource);
  599.         }
  600.  
  601.         if (err == noErr) {
  602.             s = HGetState(iitfResource);
  603.             HLock(iitfResource);
  604.             err = GetPortNameFromIITF(*iitfResource, GetHandleSize(iitfResource), portName);
  605.             HSetState(iitfResource, s);
  606.         }
  607.         
  608.         CloseResFile(prefResFile);
  609.         OTAssert("GetTCPInfoFromFile: Failed to close prefResFile", ResError() == noErr);
  610.     }
  611.     
  612.     UseResFile(oldResFile);
  613.     OTAssert("GetTCPInfoFromFile: Could not re-establish CurResFile", ResError() == noErr);
  614.     
  615.     return err;
  616. }
  617.  
  618. /////////////////////////////////////////////////////////////////
  619. // Code that's common to both implementations.
  620.  
  621. static OSStatus GetTCPInfo(Boolean *enabled, char *portName)
  622.     // A dispatcher.  If the config database is available,
  623.     // we call it, otherwise we fall back to reading the
  624.     // preferences file directly.
  625. {
  626.     OSStatus err;
  627.     
  628.     if ( (void *) OTCfgOpenDatabase == (void *) kUnresolvedCFragSymbolAddress) {
  629.         err = GetTCPInfoFromFile(enabled, portName);
  630.     } else {
  631.         err = GetTCPInfoUsingAPI(enabled, portName);
  632.     }
  633.     return err;
  634. }
  635.  
  636. // If you set kUseInetInterfaceInfo to false, OTTCPWillDial will not
  637. // use the heuristic of "if the TCP/IP stack is loaded, it's safe
  638. // to open an endpoint".  This is especially useful when debugging.
  639.  
  640. const Boolean kUseInetInterfaceInfo = true;
  641.  
  642. extern OSStatus OTTCPWillDial(UInt32 *willDial)
  643.     // The main entry point.  We call our core
  644.     // implementation and then generate the result
  645.     // based on the returned information.
  646. {
  647.     OSStatus err;
  648.     InetInterfaceInfo info;
  649.     Boolean enabled;
  650.     char currentPortName[kMaxProviderNameSize];
  651.     OTPortRecord portRecord;
  652.     
  653.     OTAssert("OTTCPWillDial: paramErr", willDial != nil);
  654.     
  655.     *willDial = kOTTCPDialUnknown;
  656.     
  657.     err = noErr;
  658.     if ( kUseInetInterfaceInfo && OTInetGetInterfaceInfo(&info, kDefaultInetInterface) == noErr) {
  659.     
  660.         // The TCP/IP stack is already loaded.  With the current
  661.         // way TCP/IP is organised, the stack being loaded implies
  662.         // that we're already dialled in.
  663.         
  664.         *willDial = kOTTCPDialNo;
  665.         
  666.     } else {
  667.         err = GetTCPInfo(&enabled, currentPortName);
  668.         if (err == noErr) {
  669.             if (enabled) {
  670.                 if ( OTStrEqual(currentPortName, "ddp") ) { 
  671.  
  672.                     // A special case for MacIP, because "ddp" does
  673.                     // not have an active port if AppleTalk is disabled.
  674.                     
  675.                     *willDial = kOTTCPDialNo;
  676.                     
  677.                 } else if ( OTFindPort(&portRecord, currentPortName) ) {
  678.                 
  679.                     // We know the port.  Look at the device type
  680.                     // to decide whether we might dial.
  681.                 
  682.                     switch ( OTGetDeviceTypeFromPortRef(portRecord.fRef) ) {
  683.                         case kOTADEVDevice:
  684.                         case kOTIRTalkDevice:
  685.                         case kOTSMDSDevice:
  686.                             OTDebugBreak("OTTCPWillDial: TCP shouldn't be using this link type");
  687.                             *willDial = kOTTCPDialNo;
  688.                             break;
  689.                             
  690.                         case kOTISDNDevice:
  691.                         case kOTATMDevice:
  692.                         case kOTSerialDevice:
  693.                         case kOTModemDevice:
  694.                             OTDebugBreak("OTTCPWillDial: TCP shouldn't be using this link type");
  695.                             *willDial = kOTTCPDialYes;
  696.                             break;
  697.  
  698.                         case kOTLocalTalkDevice:
  699.                         case kOTTokenRingDevice:
  700.                         case kOTEthernetDevice:
  701.                         case kOTFastEthernetDevice:
  702.                         case kOTFDDIDevice:
  703.                         case kOTIrDADevice:
  704.                         case kOTATMSNAPDevice:
  705.                         case kOTFibreChannelDevice:
  706.                         case kOTFireWireDevice:
  707.                             *willDial = kOTTCPDialNo;
  708.                             break;
  709.  
  710.                         case kOTMDEVDevice:
  711.                         case kOTSLIPDevice:
  712.                         case kOTPPPDevice:
  713.                             *willDial = kOTTCPDialYes;
  714.                             break;
  715.  
  716.                         default:
  717.                             OTAssert("OTTCPWillDial", *willDial == kOTTCPDialUnknown);
  718.                             break;
  719.                     }
  720.                 } else {
  721.                     err = -1;
  722.                 }
  723.             } else {
  724.                 *willDial = kOTTCPDialTCPDisabled;
  725.             }
  726.         }
  727.     }
  728.     
  729.     return err;
  730. }
  731.